home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / ANSI / c-client / pop3.c < prev    next >
C/C++ Source or Header  |  1996-10-22  |  35KB  |  1,232 lines

  1. /*
  2.  * Program:    Post Office Protocol 3 (POP3) client routines
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    6 June 1994
  13.  * Last Edited:    22 October 1996
  14.  *
  15.  * Copyright 1996 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notices appear in all copies and that both the
  20.  * above copyright notices and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  30.  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN
  32.  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. #include "mail.h"
  38. #include "osdep.h"
  39. #include <ctype.h>
  40. #include <stdio.h>
  41. #include <errno.h>
  42. extern int errno;        /* just in case */
  43. #include "pop3.h"
  44. #include "rfc822.h"
  45. #include "misc.h"
  46.  
  47. /* POP3 mail routines */
  48.  
  49.  
  50. /* Driver dispatch used by MAIL */
  51.  
  52. DRIVER pop3driver = {
  53.   "pop3",            /* driver name */
  54.   (DRIVER *) NIL,        /* next driver */
  55.   pop3_valid,            /* mailbox is valid for us */
  56.   pop3_parameters,        /* manipulate parameters */
  57.   pop3_find,            /* find mailboxes */
  58.   pop3_find_bboards,        /* find bboards */
  59.   pop3_find_all,        /* find all mailboxes */
  60.   pop3_find_all_bboards,    /* find all bboards */
  61.   pop3_subscribe,        /* subscribe to mailbox */
  62.   pop3_unsubscribe,        /* unsubscribe from mailbox */
  63.   pop3_subscribe_bboard,    /* subscribe to bboard */
  64.   pop3_unsubscribe_bboard,    /* unsubscribe from bboard */
  65.   pop3_create,            /* create mailbox */
  66.   pop3_delete,            /* delete mailbox */
  67.   pop3_rename,            /* rename mailbox */
  68.   pop3_open,            /* open mailbox */
  69.   pop3_close,            /* close mailbox */
  70.   pop3_fetchfast,        /* fetch message "fast" attributes */
  71.   pop3_fetchflags,        /* fetch message flags */
  72.   pop3_fetchstructure,        /* fetch message envelopes */
  73.   pop3_fetchheader,        /* fetch message header only */
  74.   pop3_fetchtext,        /* fetch message body only */
  75.   pop3_fetchbody,        /* fetch message body section */
  76.   pop3_setflag,            /* set message flag */
  77.   pop3_clearflag,        /* clear message flag */
  78.   pop3_search,            /* search for message based on criteria */
  79.   pop3_ping,            /* ping mailbox to see if still alive */
  80.   pop3_check,            /* check for new messages */
  81.   pop3_expunge,            /* expunge deleted messages */
  82.   pop3_copy,            /* copy messages to another mailbox */
  83.   pop3_move,            /* move messages to another mailbox */
  84.   pop3_append,            /* append string message to mailbox */
  85.   pop3_gc            /* garbage collect stream */
  86. };
  87.  
  88.                 /* prototype stream */
  89. MAILSTREAM pop3proto = {&pop3driver};
  90.  
  91.                 /* driver parameters */
  92. static long pop3_maxlogintrials = MAXLOGINTRIALS;
  93. static long pop3_port = 0;
  94. static long pop3_loginfullname = NIL;
  95.  
  96. /* POP3 mail validate mailbox
  97.  * Accepts: mailbox name
  98.  * Returns: our driver if name is valid, NIL otherwise
  99.  */
  100.  
  101. DRIVER *pop3_valid (char *name)
  102. {
  103.   DRIVER *drv = NIL;
  104.   char mbx[MAILTMPLEN];
  105.   return ((*name != '*') && (drv = mail_valid_net (name,&pop3driver,NIL,mbx))
  106.       && !strcmp (ucase (mbx),"INBOX")) ? drv : NIL;
  107. }
  108.  
  109.  
  110. /* News manipulate driver parameters
  111.  * Accepts: function code
  112.  *        function-dependent value
  113.  * Returns: function-dependent return value
  114.  */
  115.  
  116. void *pop3_parameters (long function,void *value)
  117. {
  118.   switch ((int) function) {
  119.   case SET_MAXLOGINTRIALS:
  120.     pop3_maxlogintrials = (long) value;
  121.     break;
  122.   case GET_MAXLOGINTRIALS:
  123.     value = (void *) pop3_maxlogintrials;
  124.     break;
  125.   case SET_POP3PORT:
  126.     pop3_port = (long) value;
  127.     break;
  128.   case GET_POP3PORT:
  129.     value = (void *) pop3_port;
  130.     break;
  131.   case SET_LOGINFULLNAME:
  132.     pop3_loginfullname = (long) value;
  133.     break;
  134.   case GET_LOGINFULLNAME:
  135.     value = (void *) pop3_loginfullname;
  136.     break;
  137.   default:
  138.     value = NIL;        /* error case */
  139.     break;
  140.   }
  141.   return value;
  142. }
  143.  
  144. /* POP3 mail find list of mailboxes
  145.  * Accepts: mail stream
  146.  *        pattern to search
  147.  */
  148.  
  149. void pop3_find (MAILSTREAM *stream,char *pat)
  150. {
  151.   /* Always a no-op */
  152. }
  153.  
  154.  
  155. /* POP3 mail find list of bboards
  156.  * Accepts: mail stream
  157.  *        pattern to search
  158.  */
  159.  
  160. void pop3_find_bboards (MAILSTREAM *stream,char *pat)
  161. {
  162.   /* Always a no-op */
  163. }
  164.  
  165. /* POP3 mail find list of all mailboxes
  166.  * Accepts: mail stream
  167.  *        pattern to search
  168.  */
  169.  
  170. void pop3_find_all (MAILSTREAM *stream,char *pat)
  171. {
  172.   char *t = NIL,tmp[MAILTMPLEN];
  173.   if (stream && LOCAL) {    /* have a mailbox stream open? */
  174.                 /* always include INBOX for consistency */
  175.     sprintf (tmp,"{%s/POP3}INBOX",LOCAL->host);
  176.     if (pmatch (((*pat == '{') && (t = strchr (pat,'}'))) ? ++t : pat,"INBOX"))
  177.       mm_mailbox (tmp);
  178.   }
  179. }
  180.  
  181.  
  182. /* POP3 mail find list of all bboards
  183.  * Accepts: mail stream
  184.  *        pattern to search
  185.  */
  186.  
  187. void pop3_find_all_bboards (MAILSTREAM *stream,char *pat)
  188. {
  189.   /* Always a no-op */ 
  190. }
  191.  
  192. /* POP3 mail subscribe to mailbox
  193.  * Accepts: mail stream
  194.  *        mailbox to add to subscription list
  195.  * Returns: T on success, NIL on failure
  196.  */
  197.  
  198. long pop3_subscribe (MAILSTREAM *stream,char *mailbox)
  199. {
  200.   return sm_subscribe (mailbox);
  201. }
  202.  
  203.  
  204. /* POP3 mail unsubscribe to mailbox
  205.  * Accepts: mail stream
  206.  *        mailbox to delete from subscription list
  207.  * Returns: T on success, NIL on failure
  208.  */
  209.  
  210. long pop3_unsubscribe (MAILSTREAM *stream,char *mailbox)
  211. {
  212.   return sm_unsubscribe (mailbox);
  213. }
  214.  
  215.  
  216. /* POP3 mail subscribe to bboard
  217.  * Accepts: mail stream
  218.  *        bboard to add to subscription list
  219.  * Returns: T on success, NIL on failure
  220.  */
  221.  
  222. long pop3_subscribe_bboard (MAILSTREAM *stream,char *mailbox)
  223. {
  224.   return NIL;            /* never valid for POP3 */
  225. }
  226.  
  227.  
  228. /* POP3 mail unsubscribe to bboard
  229.  * Accepts: mail stream
  230.  *        bboard to delete from subscription list
  231.  * Returns: T on success, NIL on failure
  232.  */
  233.  
  234. long pop3_unsubscribe_bboard (MAILSTREAM *stream,char *mailbox)
  235. {
  236.   return NIL;            /* never valid for POP3 */
  237. }
  238.  
  239. /* POP3 mail create mailbox
  240.  * Accepts: mail stream
  241.  *        mailbox name to create
  242.  * Returns: T on success, NIL on failure
  243.  */
  244.  
  245. long pop3_create (MAILSTREAM *stream,char *mailbox)
  246. {
  247.   return NIL;            /* never valid for POP3 */
  248. }
  249.  
  250.  
  251. /* POP3 mail delete mailbox
  252.  *        mailbox name to delete
  253.  * Returns: T on success, NIL on failure
  254.  */
  255.  
  256. long pop3_delete (MAILSTREAM *stream,char *mailbox)
  257. {
  258.   return NIL;            /* never valid for POP3 */
  259. }
  260.  
  261.  
  262. /* POP3 mail rename mailbox
  263.  * Accepts: mail stream
  264.  *        old mailbox name
  265.  *        new mailbox name
  266.  * Returns: T on success, NIL on failure
  267.  */
  268.  
  269. long pop3_rename (MAILSTREAM *stream,char *old,char *new)
  270. {
  271.   return NIL;            /* never valid for POP3 */
  272. }
  273.  
  274. /* POP3 mail open
  275.  * Accepts: stream to open
  276.  * Returns: stream on success, NIL on failure
  277.  */
  278.  
  279. MAILSTREAM *pop3_open (MAILSTREAM *stream)
  280. {
  281.   long i,nmsgs;
  282.   char *s,tmp[MAILTMPLEN],usrnam[MAILTMPLEN],pwd[MAILTMPLEN];
  283.   NETMBX mb;
  284.   MESSAGECACHE *elt;
  285.   struct hostent *host_name;
  286.                 /* return prototype for OP_PROTOTYPE call */
  287.   if (!stream) return &pop3proto;
  288.   mail_valid_net_parse (stream->mailbox,&mb);
  289.   if (LOCAL) {            /* if recycle stream */
  290.     sprintf (tmp,"Closing connection to %s",LOCAL->host);
  291.     if (!stream->silent) mm_log (tmp,(long) NIL);
  292.     pop3_close (stream);    /* do close action */
  293.     stream->dtb = &pop3driver;    /* reattach this driver */
  294.     mail_free_cache (stream);    /* clean up cache */
  295.   }
  296.                 /* in case /debug switch given */
  297.   if (mb.dbgflag) stream->debug = T;
  298.                 /* set up host with port override */
  299.   if (mb.port || pop3_port) sprintf (s = tmp,"%s:%ld",mb.host,
  300.                     mb.port ? mb.port : pop3_port);
  301.   else s = mb.host;        /* simple host name */
  302.   stream->local = fs_get (sizeof (POP3LOCAL));
  303.   LOCAL->host = cpystr (mb.host);
  304.   stream->sequence++;        /* bump sequence number */
  305.   LOCAL->response = LOCAL->reply = LOCAL->buf = NIL;
  306.   LOCAL->header = LOCAL->body = NIL;
  307.  
  308.                 /* try to open connection */
  309.   if (!((LOCAL->tcpstream = tcp_open (s,"pop3",POP3TCPPORT)) &&
  310.     pop3_reply (stream))) {
  311.     if (LOCAL->reply) mm_log (LOCAL->reply,ERROR);
  312.     pop3_close (stream);    /* failed, clean up */
  313.   }
  314.   else {            /* got connection */
  315.     mm_log (LOCAL->reply,NIL);    /* give greeting */
  316.                 /* only so many tries to login */
  317.     for (i = 0; i < pop3_maxlogintrials; ++i) {
  318.       *pwd = 0;            /* get password */
  319.       mm_login (pop3_loginfullname ? stream->mailbox :
  320.         tcp_host (LOCAL->tcpstream),usrnam,pwd,i);
  321.                 /* abort if he refuses to give a password */
  322.       if (*pwd == '\0') i = pop3_maxlogintrials;
  323.       else {            /* send login sequence */
  324.     if (pop3_send (stream,"USER",usrnam) && pop3_send (stream,"PASS",pwd))
  325.       break;        /* login successful */
  326.                 /* output failure and try again */
  327.     mm_log (LOCAL->reply,WARN);
  328.       }
  329.     }
  330.                 /* give up if too many failures */
  331.     if (i >=  pop3_maxlogintrials) {
  332.       mm_log (*pwd ? "Too many login failures":"Login aborted",ERROR);
  333.       pop3_close (stream);
  334.     }
  335.     else if (pop3_send (stream,"STAT",NIL)) {
  336.       LOCAL->buf = (char *) fs_get ((LOCAL->buflen = MAXMESSAGESIZE) + 1);
  337.       nmsgs = strtol (LOCAL->reply,NIL,10);
  338.                 /* create caches */
  339.       LOCAL->header = (char **) fs_get (nmsgs * sizeof (char *));
  340.       LOCAL->body = (char **) fs_get (nmsgs * sizeof (char *));
  341.       for (i = 0; i < nmsgs;) {    /* initialize caches */
  342.     LOCAL->header[i] = LOCAL->body[i] = NIL;
  343.                 /* instantiate elt */
  344.     elt = mail_elt (stream,++i);
  345.     elt->valid = elt->recent = T;
  346.       }
  347.       mail_exists(stream,nmsgs);/* notify upper level that messages exist */
  348.       mail_recent (stream,nmsgs);
  349.                 /* notify if empty */
  350.       if (!(nmsgs || stream->silent)) mm_log ("Mailbox is empty",WARN);
  351.     }
  352.     else {            /* error in STAT */
  353.       mm_log (LOCAL->reply,ERROR);
  354.       pop3_close (stream);    /* too bad */
  355.     }
  356.   }
  357.   return LOCAL ? stream : NIL;    /* if stream is alive, return to caller */
  358. }
  359.  
  360. /* POP3 mail close
  361.  * Accepts: MAIL stream
  362.  */
  363.  
  364. void pop3_close (MAILSTREAM *stream)
  365. {
  366.   if (LOCAL) {            /* only if a file is open */
  367.     if (LOCAL->tcpstream) {    /* close POP3 connection */
  368.       pop3_send (stream,"QUIT",NIL);
  369.       mm_notify (stream,LOCAL->reply,BYE);
  370.     }
  371.                 /* close POP3 connection */
  372.     if (LOCAL->tcpstream) tcp_close (LOCAL->tcpstream);
  373.     if (LOCAL->host) fs_give ((void **) &LOCAL->host);
  374.     if (LOCAL->response) fs_give ((void **) &LOCAL->response);
  375.                 /* free local scratch buffer */
  376.     if (LOCAL->buf) fs_give ((void **) &LOCAL->buf);
  377.     pop3_gc (stream,GC_TEXTS);    /* free local cache */
  378.     if (LOCAL->header) fs_give ((void **) &LOCAL->header);
  379.     if (LOCAL->body) fs_give ((void **) &LOCAL->body);
  380.                 /* nuke the local data */
  381.     fs_give ((void **) &stream->local);
  382.     stream->dtb = NIL;        /* log out the DTB */
  383.   }
  384. }
  385.  
  386. /* POP3 mail fetch fast information
  387.  * Accepts: MAIL stream
  388.  *        sequence
  389.  */
  390.  
  391. void pop3_fetchfast (MAILSTREAM *stream,char *sequence)
  392. {
  393.   long i;
  394.   BODY *b;
  395.                 /* ugly and slow */
  396.   if (stream && LOCAL && mail_sequence (stream,sequence))
  397.     for (i = 1; i <= stream->nmsgs; i++)
  398.       if (mail_elt (stream,i)->sequence)
  399.     pop3_fetchstructure (stream,i,&b);
  400. }
  401.  
  402.  
  403. /* POP3 mail fetch flags
  404.  * Accepts: MAIL stream
  405.  *        sequence
  406.  */
  407.  
  408. void pop3_fetchflags (MAILSTREAM *stream,char *sequence)
  409. {
  410.   return;            /* no-op for local mail */
  411. }
  412.  
  413. /* POP3 mail fetch envelope
  414.  * Accepts: MAIL stream
  415.  *        message # to fetch
  416.  *        pointer to return body
  417.  * Returns: envelope of this message, body returned in body value
  418.  *
  419.  * Fetches the "fast" information as well
  420.  */
  421.  
  422. ENVELOPE *pop3_fetchstructure (MAILSTREAM *stream,long msgno,BODY **body)
  423. {
  424.   char *h,*t;
  425.   LONGCACHE *lelt;
  426.   ENVELOPE **env;
  427.   STRING bs;
  428.   BODY **b;
  429.   unsigned long hdrsize;
  430.   unsigned long textsize = 0;
  431.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  432.   if (stream->scache) {        /* short cache */
  433.     if (msgno != stream->msgno){/* flush old poop if a different message */
  434.       mail_free_envelope (&stream->env);
  435.       mail_free_body (&stream->body);
  436.     }
  437.     stream->msgno = msgno;
  438.     env = &stream->env;        /* get pointers to envelope and body */
  439.     b = &stream->body;
  440.   }
  441.   else {            /* long cache */
  442.     lelt = mail_lelt (stream,msgno);
  443.     env = &lelt->env;        /* get pointers to envelope and body */
  444.     b = &lelt->body;
  445.   }
  446.   if ((body && !*b) || !*env) {    /* have the poop we need? */
  447.     mail_free_envelope (env);    /* flush old envelope and body */
  448.     mail_free_body (b);
  449.     hdrsize = strlen (h = pop3_fetchheader (stream,msgno));
  450.     if (body) {            /* only if want to parse body */
  451.       textsize = strlen (t = pop3_fetchtext_work (stream,msgno));
  452.                 /* calculate message size */
  453.       elt->rfc822_size = hdrsize + textsize;
  454.       INIT (&bs,mail_string,(void *) t,textsize);
  455.     }
  456.                 /* parse envelope and body */
  457.     rfc822_parse_msg (env,body ? b : NIL,h,hdrsize,body ? &bs : NIL,
  458.               tcp_localhost (LOCAL->tcpstream),LOCAL->buf);
  459.                 /* parse date */
  460.     if (*env && (*env)->date) mail_parse_date (elt,(*env)->date);
  461.     if (!elt->month) mail_parse_date (elt,"01-JAN-1969 00:00:00 GMT");
  462.   }
  463.   if (body) *body = *b;        /* return the body */
  464.   return *env;            /* return the envelope */
  465. }
  466.  
  467. /* POP3 mail fetch message header
  468.  * Accepts: MAIL stream
  469.  *        message # to fetch
  470.  * Returns: message header in RFC822 format
  471.  */
  472.  
  473. char *pop3_fetchheader (MAILSTREAM *stream,long msgno)
  474. {
  475.   char *s,*t;
  476.   unsigned long i;
  477.   unsigned long bufpos = 0;
  478.   long m = msgno - 1;
  479.   if (!LOCAL->header[m]) {    /* fetch header if don't have already */
  480.     if (pop3_send_num (stream,"RETR",msgno)) {
  481.       while (s = tcp_getline (LOCAL->tcpstream)) {
  482.     if (*s == '.') {    /* possible end of text? */
  483.       if (s[1]) t = s + 1;    /* pointer to true start of line */
  484.       else break;        /* end of data */
  485.     }
  486.     else t = s;        /* want the entire line */
  487.                 /* ensure have enough room */
  488.     if (LOCAL->buflen < (bufpos + (i = strlen (t)) + 5))
  489.       fs_resize ((void **) &LOCAL->buf,
  490.              LOCAL->buflen += (MAXMESSAGESIZE + 1));
  491.                 /* copy the text */
  492.     strncpy (LOCAL->buf + bufpos,t,i);
  493.     bufpos += i;        /* set new buffer position */
  494.     LOCAL->buf[bufpos++] = '\015';
  495.     LOCAL->buf[bufpos++] = '\012';
  496.                 /* found end of header? */
  497.     if (!(i || LOCAL->header[m])) {
  498.       LOCAL->buf[bufpos++] = '\0';
  499.       LOCAL->header[m] = cpystr (LOCAL->buf);
  500.       bufpos = 0;        /* restart buffer collection */
  501.     }
  502.     fs_give ((void **) &s);    /* free the line */
  503.       }
  504.                 /* add final newline */
  505.       LOCAL->buf[bufpos++] = '\015';
  506.       LOCAL->buf[bufpos++] = '\012';
  507.                 /* tie off string with NUL */
  508.       LOCAL->buf[bufpos++] = '\0';
  509.       if (LOCAL->header[m]) LOCAL->body[m] = cpystr (LOCAL->buf);
  510.       else LOCAL->header[m] = cpystr (LOCAL->buf);
  511.     }
  512.     else mail_elt (stream,msgno)->deleted = T;
  513.   }
  514.   return LOCAL->header[m] ? LOCAL->header[m] : "";
  515. }
  516.  
  517. /* POP3 mail fetch message text (body only)
  518.  * Accepts: MAIL stream
  519.  *        message # to fetch
  520.  * Returns: message text in RFC822 format
  521.  */
  522.  
  523. char *pop3_fetchtext (MAILSTREAM *stream,long msgno)
  524. {
  525.                 /* mark as seen */
  526.   mail_elt (stream,msgno)->seen = T;
  527.   return pop3_fetchtext_work (stream,msgno);
  528. }
  529.  
  530.  
  531. /* POP3 mail fetch message text work routine
  532.  * Accepts: MAIL stream
  533.  *        message # to fetch
  534.  * Returns: message text in RFC822 format
  535.  */
  536.  
  537. char *pop3_fetchtext_work (MAILSTREAM *stream,long msgno)
  538. {
  539.   long m = msgno - 1;
  540.                 /* fetch body if don't have already */
  541.   if (!LOCAL->header[m]) pop3_fetchheader (stream,msgno);
  542.   return LOCAL->body[m] ? LOCAL->body[m] : "";
  543. }
  544.  
  545. /* POP3 fetch message body as a structure
  546.  * Accepts: Mail stream
  547.  *        message # to fetch
  548.  *        section specifier
  549.  *        pointer to length
  550.  * Returns: pointer to section of message body
  551.  */
  552.  
  553. char *pop3_fetchbody (MAILSTREAM *stream,long m,char *s,unsigned long *len)
  554. {
  555.   BODY *b;
  556.   PART *pt;
  557.   unsigned long i;
  558.   char *base;
  559.   unsigned long offset = 0;
  560.   MESSAGECACHE *elt = mail_elt (stream,m);
  561.                 /* make sure have a body */
  562.   if (!(pop3_fetchstructure (stream,m,&b) && b && s && *s &&
  563.     ((i = strtol (s,&s,10)) > 0) &&
  564.     (base = pop3_fetchtext_work (stream,m))))
  565.     return NIL;
  566.   do {                /* until find desired body part */
  567.                 /* multipart content? */
  568.     if (b->type == TYPEMULTIPART) {
  569.       pt = b->contents.part;    /* yes, find desired part */
  570.       while (--i && (pt = pt->next));
  571.       if (!pt) return NIL;    /* bad specifier */
  572.                 /* note new body, check valid nesting */
  573.       if (((b = &pt->body)->type == TYPEMULTIPART) && !*s) return NIL;
  574.       offset = pt->offset;    /* get new offset */
  575.     }
  576.     else if (i != 1) return NIL;/* otherwise must be section 1 */
  577.                 /* need to go down further? */
  578.     if (i = *s) switch (b->type) {
  579.     case TYPEMESSAGE:        /* embedded message, calculate new base */
  580.       offset = b->contents.msg.offset;
  581.       b = b->contents.msg.body;    /* get its body, drop into multipart case */
  582.     case TYPEMULTIPART:        /* multipart, get next section */
  583.       if ((*s++ == '.') && (i = strtol (s,&s,10)) > 0) break;
  584.     default:            /* bogus subpart specification */
  585.       return NIL;
  586.     }
  587.   } while (i);
  588.                 /* lose if body bogus */
  589.   if ((!b) || b->type == TYPEMULTIPART) return NIL;
  590.   elt->seen = T;        /* mark as seen */
  591.   return rfc822_contents (&LOCAL->buf,&LOCAL->buflen,len,base + offset,
  592.               b->size.ibytes,b->encoding);
  593. }
  594.  
  595. /* POP3 mail set flag
  596.  * Accepts: MAIL stream
  597.  *        sequence
  598.  *        flag(s)
  599.  */
  600.  
  601. void pop3_setflag (MAILSTREAM *stream,char *sequence,char *flag)
  602. {
  603.   MESSAGECACHE *elt;
  604.   long i;
  605.   short f = pop3_getflags (stream,flag);
  606.   if (!f) return;        /* no-op if no flags to modify */
  607.                 /* get sequence and loop on it */
  608.   if (mail_sequence (stream,sequence)) for (i = 0; i < stream->nmsgs; i++)
  609.     if ((elt = mail_elt (stream,i + 1))->sequence) {
  610.       if (f&fSEEN) elt->seen=T;    /* set all requested flags */
  611.       if (f&fDELETED) {        /* deletion also purges the cache */
  612.     elt->deleted = T;    /* mark deleted */
  613.     if (LOCAL->header[i]) fs_give ((void **) &LOCAL->header[i]);
  614.     LOCAL->body[i] = NIL;
  615.       }
  616.       if (f&fFLAGGED) elt->flagged = T;
  617.       if (f&fANSWERED) elt->answered = T;
  618.     }
  619. }
  620.  
  621.  
  622. /* POP3 mail clear flag
  623.  * Accepts: MAIL stream
  624.  *        sequence
  625.  *        flag(s)
  626.  */
  627.  
  628. void pop3_clearflag (MAILSTREAM *stream,char *sequence,char *flag)
  629. {
  630.   MESSAGECACHE *elt;
  631.   long i;
  632.   short f = pop3_getflags (stream,flag);
  633.   if (!f) return;        /* no-op if no flags to modify */
  634.                 /* get sequence and loop on it */
  635.   if (mail_sequence (stream,sequence)) for (i = 0; i < stream->nmsgs; i++)
  636.     if ((elt = mail_elt (stream,i + 1))->sequence) {
  637.                 /* clear all requested flags */
  638.       if (f&fSEEN) elt->seen = NIL;
  639.       if (f&fDELETED) elt->deleted = NIL;
  640.       if (f&fFLAGGED) elt->flagged = NIL;
  641.       if (f&fANSWERED) elt->answered = NIL;
  642.     }
  643. }
  644.  
  645. /* POP3 mail search for messages
  646.  * Accepts: MAIL stream
  647.  *        search criteria
  648.  */
  649.  
  650. void pop3_search (MAILSTREAM *stream,char *criteria)
  651. {
  652.   long i,n;
  653.   char *d;
  654.   search_t f;
  655.                 /* initially all searched */
  656.   for (i = 1; i <= stream->nmsgs; ++i) mail_elt (stream,i)->searched = T;
  657.                 /* get first criterion */
  658.   if (criteria && (criteria = strtok (criteria," "))) {
  659.                 /* for each criterion */
  660.     for (; criteria; (criteria = strtok (NIL," "))) {
  661.       f = NIL; d = NIL; n = 0;    /* init then scan the criterion */
  662.       switch (*ucase (criteria)) {
  663.       case 'A':            /* possible ALL, ANSWERED */
  664.     if (!strcmp (criteria+1,"LL")) f = pop3_search_all;
  665.     else if (!strcmp (criteria+1,"NSWERED")) f = pop3_search_answered;
  666.     break;
  667.       case 'B':            /* possible BCC, BEFORE, BODY */
  668.     if (!strcmp (criteria+1,"CC"))
  669.       f = pop3_search_string (pop3_search_bcc,&d,&n);
  670.     else if (!strcmp (criteria+1,"EFORE"))
  671.       f = pop3_search_date (pop3_search_before,&n);
  672.     else if (!strcmp (criteria+1,"ODY"))
  673.       f = pop3_search_string (pop3_search_body,&d,&n);
  674.     break;
  675.       case 'C':            /* possible CC */
  676.     if (!strcmp (criteria+1,"C")) 
  677.       f = pop3_search_string (pop3_search_cc,&d,&n);
  678.     break;
  679.       case 'D':            /* possible DELETED */
  680.     if (!strcmp (criteria+1,"ELETED")) f = pop3_search_deleted;
  681.     break;
  682.       case 'F':            /* possible FLAGGED, FROM */
  683.     if (!strcmp (criteria+1,"LAGGED")) f = pop3_search_flagged;
  684.     else if (!strcmp (criteria+1,"ROM"))
  685.       f = pop3_search_string (pop3_search_from,&d,&n);
  686.     break;
  687.       case 'K':            /* possible KEYWORD */
  688.     if (!strcmp (criteria+1,"EYWORD"))
  689.       f = pop3_search_flag (pop3_search_keyword,&d);
  690.     break;
  691.       case 'N':            /* possible NEW */
  692.     if (!strcmp (criteria+1,"EW")) f = pop3_search_new;
  693.     break;
  694.  
  695.       case 'O':            /* possible OLD, ON */
  696.     if (!strcmp (criteria+1,"LD")) f = pop3_search_old;
  697.     else if (!strcmp (criteria+1,"N"))
  698.       f = pop3_search_date (pop3_search_on,&n);
  699.     break;
  700.       case 'R':            /* possible RECENT */
  701.     if (!strcmp (criteria+1,"ECENT")) f = pop3_search_recent;
  702.     break;
  703.       case 'S':            /* possible SEEN, SINCE, SUBJECT */
  704.     if (!strcmp (criteria+1,"EEN")) f = pop3_search_seen;
  705.     else if (!strcmp (criteria+1,"INCE"))
  706.       f = pop3_search_date (pop3_search_since,&n);
  707.     else if (!strcmp (criteria+1,"UBJECT"))
  708.       f = pop3_search_string (pop3_search_subject,&d,&n);
  709.     break;
  710.       case 'T':            /* possible TEXT, TO */
  711.     if (!strcmp (criteria+1,"EXT"))
  712.       f = pop3_search_string (pop3_search_text,&d,&n);
  713.     else if (!strcmp (criteria+1,"O"))
  714.       f = pop3_search_string (pop3_search_to,&d,&n);
  715.     break;
  716.       case 'U':            /* possible UN* */
  717.     if (criteria[1] == 'N') {
  718.       if (!strcmp (criteria+2,"ANSWERED")) f = pop3_search_unanswered;
  719.       else if (!strcmp (criteria+2,"DELETED")) f = pop3_search_undeleted;
  720.       else if (!strcmp (criteria+2,"FLAGGED")) f = pop3_search_unflagged;
  721.       else if (!strcmp (criteria+2,"KEYWORD"))
  722.         f = pop3_search_flag (pop3_search_unkeyword,&d);
  723.       else if (!strcmp (criteria+2,"SEEN")) f = pop3_search_unseen;
  724.     }
  725.     break;
  726.       default:            /* we will barf below */
  727.     break;
  728.       }
  729.       if (!f) {            /* if can't determine any criteria */
  730.     sprintf (LOCAL->buf,"Unknown search criterion: %.80s",criteria);
  731.     mm_log (LOCAL->buf,ERROR);
  732.     return;
  733.       }
  734.                 /* run the search criterion */
  735.       for (i = 1; i <= stream->nmsgs; ++i)
  736.     if (mail_elt (stream,i)->searched && !(*f) (stream,i,d,n))
  737.       mail_elt (stream,i)->searched = NIL;
  738.     }
  739.                 /* report search results to main program */
  740.     for (i = 1; i <= stream->nmsgs; ++i)
  741.       if (mail_elt (stream,i)->searched) mail_searched (stream,i);
  742.   }
  743. }
  744.  
  745. /* POP3 mail ping mailbox
  746.  * Accepts: MAIL stream
  747.  * Returns: T if stream alive, else NIL
  748.  */
  749.  
  750. long pop3_ping (MAILSTREAM *stream)
  751. {
  752.   return pop3_send (stream,"NOOP",NIL);
  753. }
  754.  
  755.  
  756. /* POP3 mail check mailbox
  757.  * Accepts: MAIL stream
  758.  */
  759.  
  760. void pop3_check (MAILSTREAM *stream)
  761. {
  762.   if (pop3_ping (stream)) mm_log ("Check completed",NIL);
  763. }
  764.  
  765. /* POP3 mail expunge mailbox
  766.  * Accepts: MAIL stream
  767.  */
  768.  
  769. void pop3_expunge (MAILSTREAM *stream)
  770. {
  771.   MESSAGECACHE *elt;
  772.   unsigned long i,n = 0;
  773.   for (i = 1; i <= stream->nmsgs; i++)
  774.     if ((elt = mail_elt (stream,i))->deleted && !elt->data1 &&
  775.     (elt->data1 = pop3_send_num (stream,"DELE",i))) n++;
  776.   if (!stream->silent) {    /* only if not silent */
  777.     if (n) {            /* did we expunge anything? */
  778.       sprintf (LOCAL->buf,"Expunged %ld messages",n);
  779.       mm_log (LOCAL->buf,(long) NIL);
  780.     }
  781.     else mm_log ("No messages deleted, so no update needed",(long) NIL);
  782.   }
  783. }
  784.  
  785.  
  786. /* POP3 mail copy message(s)
  787.  * Accepts: MAIL stream
  788.  *        sequence
  789.  *        destination mailbox
  790.  * Returns: T if copy successful, else NIL
  791.  */
  792.  
  793. long pop3_copy (MAILSTREAM *stream,char *sequence,char *mailbox)
  794. {
  795.   mm_log ("Copy not valid for POP3",ERROR);
  796.   return NIL;
  797. }
  798.  
  799.  
  800. /* POP3 mail move message(s)
  801.  * Accepts: MAIL stream
  802.  *        sequence
  803.  *        destination mailbox
  804.  * Returns: T if move successful, else NIL
  805.  */
  806.  
  807. long pop3_move (MAILSTREAM *stream,char *sequence,char *mailbox)
  808. {
  809.   mm_log ("Move not valid for POP3",ERROR);
  810.   return NIL;
  811. }
  812.  
  813.  
  814. /* POP3 mail append message from stringstruct
  815.  * Accepts: MAIL stream
  816.  *        destination mailbox
  817.  *        stringstruct of messages to append
  818.  * Returns: T if append successful, else NIL
  819.  */
  820.  
  821. long pop3_append (MAILSTREAM *stream,char *mailbox,char *flags,char *date,
  822.           STRING *message)
  823. {
  824.   mm_log ("Append not valid for POP3",ERROR);
  825.   return NIL;
  826. }
  827.  
  828. /* POP3 garbage collect stream
  829.  * Accepts: Mail stream
  830.  *        garbage collection flags
  831.  */
  832.  
  833. void pop3_gc (MAILSTREAM *stream,long gcflags)
  834. {
  835.   unsigned long i;
  836.   if (!stream->halfopen)     /* never on half-open stream */
  837.     if (gcflags & GC_TEXTS)    /* garbage collect texts? */
  838.                 /* flush texts from cache */
  839.       for (i = 0; i < stream->nmsgs; i++) {
  840.     if (LOCAL->header[i]) fs_give ((void **) &LOCAL->header[i]);
  841.     LOCAL->body[i] = NIL;
  842.       }
  843. }
  844.  
  845. /* Internal routines */
  846.  
  847.  
  848. /* Post Office Protocol 3 send command with number argument
  849.  * Accepts: MAIL stream
  850.  *        command
  851.  *        number
  852.  * Returns: T if successful, NIL if failure
  853.  */
  854.  
  855. long pop3_send_num (MAILSTREAM *stream,char *command,unsigned long n)
  856. {
  857.   char tmp[MAILTMPLEN];
  858.   sprintf (tmp,"%ld",n);
  859.   return pop3_send (stream,command,tmp);
  860. }
  861.  
  862.  
  863. /* Post Office Protocol 3 send command
  864.  * Accepts: MAIL stream
  865.  *        command
  866.  *        command argument
  867.  * Returns: T if successful, NIL if failure
  868.  */
  869.  
  870. long pop3_send (MAILSTREAM *stream,char *command,char *args)
  871. {
  872.   char tmp[MAILTMPLEN];
  873.   if (!LOCAL->tcpstream) return pop3_fake (stream,"No-op dead stream");
  874.                 /* build the complete command */
  875.   if (args) sprintf (tmp,"%s %s",command,args);
  876.   else strcpy (tmp,command);
  877.   if (stream->debug) mm_dlog (tmp);
  878.   strcat (tmp,"\015\012");
  879.                 /* send the command */
  880.   return tcp_soutr (LOCAL->tcpstream,tmp) ? pop3_reply (stream) :
  881.     pop3_fake (stream,"POP3 connection broken in command");
  882. }
  883.  
  884. /* Post Office Protocol 3 get reply
  885.  * Accepts: MAIL stream
  886.  * Returns: T if success reply, NIL if error reply
  887.  */
  888.  
  889. long pop3_reply (MAILSTREAM *stream)
  890. {
  891.   char *s;
  892.   if (!LOCAL->tcpstream) return pop3_fake (stream,"No-op dead stream");
  893.                 /* flush old reply */
  894.   if (LOCAL->response) fs_give ((void **) &LOCAL->response);
  895.                   /* get reply */
  896.   if (!(LOCAL->response = tcp_getline (LOCAL->tcpstream)))
  897.     return pop3_fake (stream,"POP3 connection broken in response");
  898.   if (stream->debug) mm_dlog (LOCAL->response);
  899.   LOCAL->reply = (s = strchr (LOCAL->response,' ')) ? s + 1 : LOCAL->response;
  900.                 /* return success or failure */
  901.   return (*LOCAL->response =='+') ? T : NIL;
  902. }
  903.  
  904.  
  905. /* Post Office Protocol 3 set fake error
  906.  * Accepts: MAIL stream
  907.  *        error text
  908.  * Returns: NIL, always
  909.  */
  910.  
  911. long pop3_fake (MAILSTREAM *stream,char *text)
  912. {
  913.   mm_notify (stream,text,BYE);    /* send bye alert */
  914.   if (LOCAL->tcpstream) tcp_close (LOCAL->tcpstream);
  915.   LOCAL->tcpstream = NIL;    /* farewell, dear TCP stream */
  916.                 /* flush any old reply */
  917.   if (LOCAL->response) fs_give ((void **) &LOCAL->response);
  918.   LOCAL->reply = text;        /* set up pseudo-reply string */
  919.   return NIL;            /* return error code */
  920. }
  921.  
  922.  
  923. /* Parse flag list
  924.  * Accepts: MAIL stream
  925.  *        flag list as a character string
  926.  * Returns: flag command list
  927.  */
  928.  
  929. short pop3_getflags (MAILSTREAM *stream,char *flag)
  930. {
  931.   char *t,tmp[MAILTMPLEN],err[MAILTMPLEN];
  932.   short f = 0;
  933.   short i,j;
  934.   if (flag && *flag) {        /* no-op if no flag string */
  935.                 /* check if a list and make sure valid */
  936.     if ((i = (*flag == '(')) ^ (flag[strlen (flag)-1] == ')')) {
  937.       mm_log ("Bad flag list",ERROR);
  938.       return NIL;
  939.     }
  940.                 /* copy the flag string w/o list construct */
  941.     strncpy (tmp,flag+i,(j = strlen (flag) - (2*i)));
  942.     tmp[j] = '\0';
  943.     t = ucase (tmp);        /* uppercase only from now on */
  944.  
  945.     while (t && *t) {        /* parse the flags */
  946.       if (*t == '\\') {        /* system flag? */
  947.     switch (*++t) {        /* dispatch based on first character */
  948.     case 'S':        /* possible \Seen flag */
  949.       if (t[1] == 'E' && t[2] == 'E' && t[3] == 'N') i = fSEEN;
  950.       t += 4;        /* skip past flag name */
  951.       break;
  952.     case 'D':        /* possible \Deleted flag */
  953.       if (t[1] == 'E' && t[2] == 'L' && t[3] == 'E' && t[4] == 'T' &&
  954.           t[5] == 'E' && t[6] == 'D') i = fDELETED;
  955.       t += 7;        /* skip past flag name */
  956.       break;
  957.     case 'F':        /* possible \Flagged flag */
  958.       if (t[1] == 'L' && t[2] == 'A' && t[3] == 'G' && t[4] == 'G' &&
  959.           t[5] == 'E' && t[6] == 'D') i = fFLAGGED;
  960.       t += 7;        /* skip past flag name */
  961.       break;
  962.     case 'A':        /* possible \Answered flag */
  963.       if (t[1] == 'N' && t[2] == 'S' && t[3] == 'W' && t[4] == 'E' &&
  964.           t[5] == 'R' && t[6] == 'E' && t[7] == 'D') i = fANSWERED;
  965.       t += 8;        /* skip past flag name */
  966.       break;
  967.     default:        /* unknown */
  968.       i = 0;
  969.       break;
  970.     }
  971.                 /* add flag to flags list */
  972.     if (i && ((*t == '\0') || (*t++ == ' '))) f |= i;
  973.       }
  974.       else {            /* no user flags yet */
  975.     t = strtok (t," ");    /* isolate flag name */
  976.     sprintf (err,"Unknown flag: %.80s",t);
  977.     t = strtok (NIL," ");    /* get next flag */
  978.     mm_log (err,ERROR);
  979.       }
  980.     }
  981.   }
  982.   return f;
  983. }
  984.  
  985. /* Search support routines
  986.  * Accepts: MAIL stream
  987.  *        message number
  988.  *        pointer to additional data
  989.  *        pointer to temporary buffer
  990.  * Returns: T if search matches, else NIL
  991.  */
  992.  
  993. char pop3_search_all (MAILSTREAM *stream,long msgno,char *d,long n)
  994. {
  995.   return T;            /* ALL always succeeds */
  996. }
  997.  
  998.  
  999. char pop3_search_answered (MAILSTREAM *stream,long msgno,char *d,long n)
  1000. {
  1001.   return mail_elt (stream,msgno)->answered ? T : NIL;
  1002. }
  1003.  
  1004.  
  1005. char pop3_search_deleted (MAILSTREAM *stream,long msgno,char *d,long n)
  1006. {
  1007.   return mail_elt (stream,msgno)->deleted ? T : NIL;
  1008. }
  1009.  
  1010.  
  1011. char pop3_search_flagged (MAILSTREAM *stream,long msgno,char *d,long n)
  1012. {
  1013.   return mail_elt (stream,msgno)->flagged ? T : NIL;
  1014. }
  1015.  
  1016.  
  1017. char pop3_search_keyword (MAILSTREAM *stream,long msgno,char *d,long n)
  1018. {
  1019.   return NIL;            /* keywords not supported yet */
  1020. }
  1021.  
  1022.  
  1023. char pop3_search_new (MAILSTREAM *stream,long msgno,char *d,long n)
  1024. {
  1025.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1026.   return (elt->recent && !elt->seen) ? T : NIL;
  1027. }
  1028.  
  1029. char pop3_search_old (MAILSTREAM *stream,long msgno,char *d,long n)
  1030. {
  1031.   return mail_elt (stream,msgno)->recent ? NIL : T;
  1032. }
  1033.  
  1034.  
  1035. char pop3_search_recent (MAILSTREAM *stream,long msgno,char *d,long n)
  1036. {
  1037.   return mail_elt (stream,msgno)->recent ? T : NIL;
  1038. }
  1039.  
  1040.  
  1041. char pop3_search_seen (MAILSTREAM *stream,long msgno,char *d,long n)
  1042. {
  1043.   return mail_elt (stream,msgno)->seen ? T : NIL;
  1044. }
  1045.  
  1046.  
  1047. char pop3_search_unanswered (MAILSTREAM *stream,long msgno,char *d,long n)
  1048. {
  1049.   return mail_elt (stream,msgno)->answered ? NIL : T;
  1050. }
  1051.  
  1052.  
  1053. char pop3_search_undeleted (MAILSTREAM *stream,long msgno,char *d,long n)
  1054. {
  1055.   return mail_elt (stream,msgno)->deleted ? NIL : T;
  1056. }
  1057.  
  1058.  
  1059. char pop3_search_unflagged (MAILSTREAM *stream,long msgno,char *d,long n)
  1060. {
  1061.   return mail_elt (stream,msgno)->flagged ? NIL : T;
  1062. }
  1063.  
  1064.  
  1065. char pop3_search_unkeyword (MAILSTREAM *stream,long msgno,char *d,long n)
  1066. {
  1067.   return T;            /* keywords not supported yet */
  1068. }
  1069.  
  1070.  
  1071. char pop3_search_unseen (MAILSTREAM *stream,long msgno,char *d,long n)
  1072. {
  1073.   return mail_elt (stream,msgno)->seen ? NIL : T;
  1074. }
  1075.  
  1076. char pop3_search_before (MAILSTREAM *stream,long msgno,char *d,long n)
  1077. {
  1078.   return (char) (pop3_msgdate (stream,msgno) < n);
  1079. }
  1080.  
  1081.  
  1082. char pop3_search_on (MAILSTREAM *stream,long msgno,char *d,long n)
  1083. {
  1084.   return (char) (pop3_msgdate (stream,msgno) == n);
  1085. }
  1086.  
  1087.  
  1088. char pop3_search_since (MAILSTREAM *stream,long msgno,char *d,long n)
  1089. {
  1090.                 /* everybody interprets "since" as .GE. */
  1091.   return (char) (pop3_msgdate (stream,msgno) >= n);
  1092. }
  1093.  
  1094.  
  1095. unsigned long pop3_msgdate (MAILSTREAM *stream,long msgno)
  1096. {
  1097.   MESSAGECACHE *elt = mail_elt (stream,msgno);
  1098.                 /* get date if don't have it yet */
  1099.   if (!elt->day) pop3_fetchstructure (stream,msgno,NIL);
  1100.   return (long) (elt->year << 9) + (elt->month << 5) + elt->day;
  1101. }
  1102.  
  1103.  
  1104. char pop3_search_body (MAILSTREAM *stream,long msgno,char *d,long n)
  1105. {
  1106.   char *t = pop3_fetchtext_work (stream,msgno);
  1107.   return (t && search (t,(unsigned long) strlen (t),d,n));
  1108. }
  1109.  
  1110.  
  1111. char pop3_search_subject (MAILSTREAM *stream,long msgno,char *d,long n)
  1112. {
  1113.   char *t = pop3_fetchstructure (stream,msgno,NIL)->subject;
  1114.   return t ? search (t,strlen (t),d,n) : NIL;
  1115. }
  1116.  
  1117.  
  1118. char pop3_search_text (MAILSTREAM *stream,long msgno,char *d,long n)
  1119. {
  1120.   char *t = pop3_fetchheader (stream,msgno);
  1121.   return (t && search (t,strlen (t),d,n)) ||
  1122.     pop3_search_body (stream,msgno,d,n);
  1123. }
  1124.  
  1125. char pop3_search_bcc (MAILSTREAM *stream,long msgno,char *d,long n)
  1126. {
  1127.   ADDRESS *a = pop3_fetchstructure (stream,msgno,NIL)->bcc;
  1128.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1129.                 /* get text for address */
  1130.   rfc822_write_address (LOCAL->buf,a);
  1131.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1132. }
  1133.  
  1134.  
  1135. char pop3_search_cc (MAILSTREAM *stream,long msgno,char *d,long n)
  1136. {
  1137.   ADDRESS *a = pop3_fetchstructure (stream,msgno,NIL)->cc;
  1138.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1139.                 /* get text for address */
  1140.   rfc822_write_address (LOCAL->buf,a);
  1141.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1142. }
  1143.  
  1144.  
  1145. char pop3_search_from (MAILSTREAM *stream,long msgno,char *d,long n)
  1146. {
  1147.   ADDRESS *a = pop3_fetchstructure (stream,msgno,NIL)->from;
  1148.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1149.                 /* get text for address */
  1150.   rfc822_write_address (LOCAL->buf,a);
  1151.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1152. }
  1153.  
  1154.  
  1155. char pop3_search_to (MAILSTREAM *stream,long msgno,char *d,long n)
  1156. {
  1157.   ADDRESS *a = pop3_fetchstructure (stream,msgno,NIL)->to;
  1158.   LOCAL->buf[0] = '\0';        /* initially empty string */
  1159.                 /* get text for address */
  1160.   rfc822_write_address (LOCAL->buf,a);
  1161.   return search (LOCAL->buf,(long) strlen (LOCAL->buf),d,n);
  1162. }
  1163.  
  1164. /* Search parsers */
  1165.  
  1166.  
  1167. /* Parse a date
  1168.  * Accepts: function to return
  1169.  *        pointer to date integer to return
  1170.  * Returns: function to return
  1171.  */
  1172.  
  1173. search_t pop3_search_date (search_t f,long *n)
  1174. {
  1175.   long i;
  1176.   char *s;
  1177.   MESSAGECACHE elt;
  1178.                 /* parse the date and return fn if OK */
  1179.   return (pop3_search_string (f,&s,&i) && mail_parse_date (&elt,s) &&
  1180.       (*n = (elt.year << 9) + (elt.month << 5) + elt.day)) ? f : NIL;
  1181. }
  1182.  
  1183. /* Parse a flag
  1184.  * Accepts: function to return
  1185.  *        pointer to string to return
  1186.  * Returns: function to return
  1187.  */
  1188.  
  1189. search_t pop3_search_flag (search_t f,char **d)
  1190. {
  1191.                 /* get a keyword, return if OK */
  1192.   return (*d = strtok (NIL," ")) ? f : NIL;
  1193. }
  1194.  
  1195.  
  1196. /* Parse a string
  1197.  * Accepts: function to return
  1198.  *        pointer to string to return
  1199.  *        pointer to string length to return
  1200.  * Returns: function to return
  1201.  */
  1202.  
  1203. search_t pop3_search_string (search_t f,char **d,long *n)
  1204. {
  1205.   char *end = " ";
  1206.   char *c = strtok (NIL,"");    /* remainder of criteria */
  1207.   if (!c) return NIL;        /* missing argument */
  1208.   switch (*c) {            /* see what the argument is */
  1209.   case '{':            /* literal string */
  1210.     *n = strtol (c+1,d,10);    /* get its length */
  1211.     if ((*(*d)++ == '}') && (*(*d)++ == '\015') && (*(*d)++ == '\012') &&
  1212.     (!(*(c = *d + *n)) || (*c == ' '))) {
  1213.       char e = *--c;
  1214.       *c = DELIM;        /* make sure not a space */
  1215.       strtok (c," ");        /* reset the strtok mechanism */
  1216.       *c = e;            /* put character back */
  1217.       break;
  1218.     }
  1219.   case '\0':            /* catch bogons */
  1220.   case ' ':
  1221.     return NIL;
  1222.   case '"':            /* quoted string */
  1223.     if (strchr (c+1,'"')) end = "\"";
  1224.     else return NIL;
  1225.   default:            /* atomic string */
  1226.     if (*d = strtok (c,end)) *n = strlen (*d);
  1227.     else return NIL;
  1228.     break;
  1229.   }
  1230.   return f;
  1231. }
  1232.